﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Hive.Core
{
    public static class Env
    {
        public static int LoadTime { get; set; }
        public static int TurnTime { get; set; }

        //private static DateTime turnStart;
        //public static int TimeRemaining
        //{
        //    get
        //    {
        //        TimeSpan timeSpent = DateTime.Now - turnStart;
        //        return TurnTime - timeSpent.Milliseconds;
        //    }
        //}

        public static int ViewRadius2 { get; set; }
        public static int ViewRadius { get; set; }

        public static int AttackRadius2 { get; set; }
        public static int SpawnRadius2 { get; set; }

        public static void Init(int width, int height,
          int turntime, int loadtime,
          int viewradius2, int attackradius2, int spawnradius2)
        {
            LoadTime = loadtime;
            TurnTime = turntime;

            ViewRadius2 = viewradius2;
            ViewRadius = (int)Math.Floor(Math.Sqrt(ViewRadius2));

            AttackRadius2 = attackradius2;
            SpawnRadius2 = spawnradius2;

            Map.Init(width, height);
        }

        public static List<EnvEvent> Events = new List<EnvEvent>();

        public static readonly Map Map = new Map();

        public static List<Location> Food = new List<Location>();
        public static List<Location> MyHills = new List<Location>();
        public static List<AntHillLocation> EnemyHills = new List<AntHillLocation>();

        public static readonly List<Ant> MyAnts = new List<Ant>();

        public static readonly List<AntLocation> EnemyAnts = new List<AntLocation>();

        public static GameState CurrState;
        public static GameState PrevState;

        public static void SetState(GameState state)
        {
            PrevState = CurrState;
            CurrState = state;
        }

        public static void Update()
        {
            Events = new List<EnvEvent>();

            Map.ClearCache();
            SetLandTiles();
            FillMyAnts();
            FillEnemyAnts();
            FillHills();
            FillFood();
        }

        private static void FillEnemyAnts()
        {
            foreach (var enemy in EnemyAnts.ToList())
            {
                if (Map.IsVisible(enemy) && !CurrState.EnemyAnts.Contains(enemy))
                {
                    EnemyAnts.Remove(enemy);
                }
            }

            foreach (var deadTile in CurrState.DeadTiles)
            {
                var ant = EnemyAnts.Find(a => new Location(a).Equals(deadTile));
                if (ant != null)
                {
                    EnemyAnts.Remove(ant);
                    Log.Write(string.Format("Enemy R.I.P. {0}!!!", deadTile.ToString()));
                }
            }

            foreach (var enemyAnt in CurrState.EnemyAnts)
            {
                if (!EnemyAnts.Any(ant => ant.Equals(enemyAnt)))
                {
                    EnemyAnts.Add(enemyAnt);
                    Log.Write(string.Format("Enemy spotted at {0}!!!", enemyAnt.ToString()));
                }
            }
        }

        private static void FillFood()
        {
            foreach (var f in Food.ToList())
            {
                if (Map.IsVisible(f) && !CurrState.FoodTiles.Contains(f))
                {
                    Food.Remove(f);
                    Events.Add(new FoodRemovedEvent(f));
                }
            }
            var addedFood = CurrState.FoodTiles.Except(Food);
            Events.AddRange(addedFood.Select(f => new FoodAddedEvent(f)));
            Food.AddRange(addedFood);
        }

        private static void FillHills()
        {
            MyHills.AddRange(CurrState.MyHills.ConvertAll(ah => new Location(ah)).Except(MyHills));
            EnemyHills.AddRange(CurrState.EnemyHills.Except(EnemyHills));

            foreach (var h in EnemyHills.ToList())
            {
                if (Map.IsVisible(h) && !CurrState.EnemyHills.Contains(h))
                    EnemyHills.Remove(h);
            }
        }

        private static void FillMyAnts()
        {
            foreach (var deadTile in CurrState.DeadTiles)
            {
                var ant = MyAnts.Find(a => a.Location.Equals(deadTile));
                if (ant != null)
                {
                    MyAnts.Remove(ant);
                    Events.Add(new MyAntRemovedEvent(ant));
                    Log.Write(string.Format("R.I.P. {0} ant id {1} !!!", deadTile.ToString(), ant.Id));
                }
            }

            foreach (var myAnt in CurrState.MyAnts)
            {
                if (!MyAnts.Any(ant => ant.Location.Equals(new Location(myAnt.Row, myAnt.Col))))
                {
                    var ant = new Ant(myAnt);
                    MyAnts.Add(ant);
                    Events.Add(new MyAntAddedEvent(ant));
                    Log.Write(string.Format("New Ant at {0}[{1}]!!!", myAnt.ToString(), ant.Id));
                }
            }

            //if (currentState.MyAnts.Count != ants.Count)
            //    Log.Write(string.Format"Ants count not matching")
        }

        private static void SetLandTiles()
        {
            foreach (var ant in CurrState.MyAnts)
            {
                foreach (Location offset in GetVisibilityOffsets())
                {
                    var loc = (ant + offset).Normalize();

                    if (Map[loc] != Tile.Water)
                    {
                        Map[loc] = Tile.Land;
                    }
                }
            }
        }

        public static List<Location> GetVisibilityOffsets()
        {
            int squares = (int)Math.Floor(Math.Sqrt(ViewRadius2));
            return GetOffsets(squares);
        }

        public static List<Location> GetAttackOffsets()
        {
            int squares = (int)Math.Floor(Math.Sqrt(AttackRadius2));
            return GetOffsets(squares);
        }

        private static List<Location> GetOffsets(int squares)
        {
            List<Location> offsets = new List<Location>();
            for (int r = -1 * squares; r <= squares; ++r)
            {
                for (int c = -1 * squares; c <= squares; ++c)
                {
                    int square = r * r + c * c;
                    if (square < ViewRadius2)
                    {
                        offsets.Add(new Location(r, c));
                    }
                }
            }
            return offsets;
        }
    }
}
